#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<arpa/inet.h>
#include<ctype.h> 
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<netdb.h>
#include <fcntl.h>
#include <iostream>
#include <string>

#include "server.h"
#include "session.h"

namespace QinChat{
    namespace Net{
        CServer::CServer(){

        }

        CServer::~CServer(void){
            close(m_serverfd);
        }

        bool CServer::Init(){
            
            m_serverfd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK,IPPROTO_TCP);
            if (m_serverfd < 0){
                std::cout<<"init socket error\n";
                return false;
            }

            struct sockaddr_in server_addr;
            
            bzero(&(server_addr.sin_zero), 0);
            server_addr.sin_family= AF_INET;
            server_addr.sin_port = htons(8001);
            server_addr.sin_addr.s_addr = INADDR_ANY;
            

            if (bind(m_serverfd, (struct sockaddr*)&server_addr, sizeof(struct sockaddr)) < 0){
                std::cout<<"init bind error\n";
                return false;
            }

            if (listen(m_serverfd, 5)<0){
                std::cout<<"init listen error\n";
                return false;
            }


            return true;
        }

        void CServer::Run(void){
            char buffer[1024];
            const char* cmd_exit = "close";

            bool bExit = false;
            std::vector<int> exitsClinet ;
            while(!bExit){
                std::shared_ptr<CSession> pSession = Accepte();
                if (nullptr != pSession){
                    int fd = pSession->GetFd();
                    m_setClientFd.insert(fd);
                }
                
                for (auto var : m_setClientFd){
                    memset(buffer, 0, 1024* sizeof(char));
                    int len = ::read(var, buffer, 1024);
                    if (len < 0){
                        continue;
                    }
                    else if (0 == len ){
                        exitsClinet.push_back(var);
                        continue;
                    }

                    std::cout<<var<<" find session: "<< var << std::endl;
                    std::shared_ptr<CSession> pSession = m_sessionManager.Find(var);
                    if (nullptr == pSession){
                        Close(var);
                        continue;
                    }

                    std::cout<<var<<" session recv: "<< var << std::endl;
                    pSession->Recv(buffer, len);

                    std::cout<<var<<" recv: "<< buffer << std::endl;
                    pSession->Send(buffer, len );

                    if (0 == memcmp(buffer, cmd_exit, len)){
                        bExit = true;
                        break;
                    }
                    //sleep(10);
                }

                handleDown(exitsClinet);
            }
        }

        std::shared_ptr<CSession> CServer::Accepte(void){
            struct sockaddr_in client_addr;
            socklen_t sin_size = sizeof(client_addr);

            int client_fd = accept(m_serverfd, (struct sockaddr *)&client_addr, &sin_size);
            if (0 > client_fd){
                return nullptr;
            }
            std::cout<<"clinet_fd: " << client_fd<<std::endl;

            int flags = fcntl(client_fd, F_GETFL, 0);
            fcntl(client_fd, F_SETFL, flags | O_NONBLOCK);

            if (m_setClientFd.end() == m_setClientFd.find(client_fd)){
                std::shared_ptr<CSession> pSession = std::make_shared<CSession>(client_addr, client_fd);
                if (!m_sessionManager.Push(pSession)){
                    return (nullptr);
                }
                
                return pSession;
            }
            else{
                m_sessionManager.Remove(client_fd);
                std::shared_ptr<CSession> pSession = std::make_shared<CSession>(client_addr, client_fd);
                if (!m_sessionManager.Push(pSession)){
                    return (nullptr);
                }
                
                return pSession;
            }
        }

        void CServer::Close(int clientFd){
            std::set<int>::iterator itor = m_setClientFd.find(clientFd);
            if (m_setClientFd.end() != itor){
                close(clientFd);
            }
        }

        void CServer::handleDown(std::vector<int>& downVector){
            for (auto var : downVector){
                m_setClientFd.erase(var);
                m_sessionManager.Remove(var);
            }

            downVector.clear();
        }
    }
}
